package org.snail.attendence.service.impl;

import com.github.pagehelper.PageInfo;
import lombok.extern.log4j.Log4j;
import org.snail.attendence.bean.*;
import org.snail.attendence.cache.AttendenceCacheService;
import org.snail.attendence.job.AbnormalRecord;
import org.snail.attendence.mapper.AttendenceMapper;
import org.snail.common.CommonUtils;
import org.snail.common.DateUtils;
import org.snail.constant.CacheNameConstant;
import org.snail.constant.ErrCodeConstant;
import org.snail.department.service.impl.DepartmentServiceImpl;
import org.snail.department.service.impl.DepartmentTreeNodeService;
import org.snail.dept.bean.DepartDO;
import org.snail.dept.bean.TreeNode;
import org.snail.du.bean.DUDO;
import org.snail.du.service.impl.DuServiceImpl;
import org.snail.employee.bean.EmpCondition;
import org.snail.employee.bean.EmployeesDO;
import org.snail.employee.service.impl.EmployeesServiceImpl;
import org.snail.exception.BaseException;
import org.snail.exception.CommonException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.text.ParseException;
import java.util.*;

@Service
@Log4j
@EnableTransactionManagement
public class AttendenceOptionService {

    @Autowired
    private AttendenceMapper attendenceMapper;
    @Autowired
    private EmployeesServiceImpl employeesService;
    @Autowired
    private AttendenceCacheService attendenceCacheService;
    @Autowired
    private AbnormalRecord abnormalRecord;
    @Autowired
    private DepartmentServiceImpl departmentService;
    @Autowired
    private DepartmentTreeNodeService departmentTreeNodeService;
    @Autowired
    private DuServiceImpl duService;



    /**
     * 保存考勤的全部数据
     *
     * @param aliWorkDTOs
     * @throws ParseException
     */
    @Transactional
    public String saveAttendance(List<AliWorkDTO> aliWorkDTOs) throws ParseException, BaseException {
        String result = "保存考勤数据成功";
        saveMySQL(aliWorkDTOs);
        List<AbnormalAttendanceDTO> abnormalAttendanceDTOs = saveAttendanceAbnormal(aliWorkDTOs);
        saveAttendanceNormal(aliWorkDTOs, abnormalAttendanceDTOs);
        return result;
    }

    /**
     * 设置其他字段如 DU,是否休息日
     * @param aliWorkDTOs
     * @throws Exception
     */
    public void setOtherField(List<AliWorkDTO> aliWorkDTOs) throws Exception {
        //部门treeMap
        Map<String, TreeNode> treeMap = getTreeMap("25393462");
        for(AliWorkDTO aliWorkDTO:aliWorkDTOs){
            //设置DU
            setDU(aliWorkDTO, treeMap);
            //设置当天考勤是否为休息日
            setRest(aliWorkDTO);
        }

    }

    /**
     * 设置当天考勤是否为休息日
     * 如果当天是法定节假日 且审批单没有数据就是休息日
     *
     * @param aliWorkDTO
     */
    private void setRest(AliWorkDTO aliWorkDTO) throws ParseException {
        Date date = DateUtils.parseDateStrToDate(aliWorkDTO.getDateStr());
        String approval = aliWorkDTO.getApproval();
        if (DateUtils.isRest(date)) {
            if (StringUtils.isEmpty(approval)) {
                aliWorkDTO.setRest(1);
            }
        }
    }

    /**
     * 从数据库获取treeMap
     *
     * @param rootId 根节点部门id
     * @return
     * @throws Exception
     */
    private Map<String, TreeNode> getTreeMap(String rootId) throws Exception {
        Set<DepartDO> departDOs = departmentTreeNodeService.getTreeNodeList(Long.parseLong(rootId));
        Map<String, TreeNode> treeMap = new HashMap<String, TreeNode>();
        treeMap.putAll(departmentTreeNodeService.generateDepartmentMap(departDOs));
        return treeMap;
    }


    /**
     * 给考勤对象设置DU
     *
     * @param aliWorkDTO
     */
    private void setDU(AliWorkDTO aliWorkDTO, Map<String, TreeNode> treeMap) throws Exception {
        if (StringUtils.isEmpty(aliWorkDTO.getDu())) {

            String aliNum = aliWorkDTO.getAliNumber();
            EmpCondition empCondition = new EmpCondition();
            empCondition.setOutNum(aliNum);
            Map<String, PageInfo> map = employeesService.findAllEmp(empCondition);
            EmployeesDO employeesDO = null;
            if (map != null) {
                PageInfo o = map.get("pageInfo");
                if (o != null) {
                    List<EmployeesDO> list = (List<EmployeesDO>) o.getList();
                    if (list != null && list.size() > 0) {
                        employeesDO = list.get(0);
                    } else {
                        log.error("设置DU list为空");
                    }
                } else {
                    log.error("设置DU map.get(\"pageInfo\")失败");
                }
            } else {
                log.error("设置DU 根据外包工号查询人员表失败");
            }
            if(employeesDO!=null){
                String userId = employeesDO.getDingId();
                String deptId = departmentService.findDeptIdByUserId(userId) + "";
                getDUFromTreeMap(aliWorkDTO,deptId,treeMap,10);
            }

        }
    }

    /**
     * 获取DU
     *
     * @param deptId  部门ID
     * @param treeMap 部门ID对应一个TreeNode
     * @return
     */
    private void getDUFromTreeMap(AliWorkDTO aliWorkDTO, String deptId, Map<String, TreeNode> treeMap, int loop) {
        if (loop > 0 && !StringUtils.isEmpty(deptId)) {
            TreeNode treeNode = treeMap.get(deptId);
            if (treeNode != null) {
                String parentId = treeNode.getParentId();
                String name = treeNode.getName();
                if (!StringUtils.isEmpty(name)) {
                    if (name.contains("DU")) {
                        aliWorkDTO.setDu(name);
                    } else {
                        getDUFromTreeMap(aliWorkDTO,parentId,treeMap,--loop);
                    }
                }

            }
        }

    }

    /**
     * 保存异常考勤数据到数据库
     * 同时保存到redis中
     *
     * @param aliWorkDTOs
     * @throws BaseException
     * @throws ParseException
     */
    public List<AbnormalAttendanceDTO> saveAttendanceAbnormal(List<AliWorkDTO> aliWorkDTOs) throws BaseException, ParseException {

        //判断是否重复导入考勤,通过state来判断0就是不变，1可以新增 2 修改
        //如果不处理导入一次考勤就存一次异常这样不正确
        List<AliWorkDTO> insertList = filterAliWorkChange(aliWorkDTOs);

        List<AbnormalAttendanceDTO> abnormalAttendanceDTOs = abnormalRecord.getAbnormalAttendance(insertList);
        List<AbnormalAttendanceDTO> abnormalAttendanceDTOs_insert = new ArrayList<>();
        List<AbnormalAttendanceDTO> abnormalAttendanceDTOs_update = new ArrayList<>();

        if (abnormalAttendanceDTOs.size() > 0) {
            for (AbnormalAttendanceDTO abnormalAttendanceDTO : abnormalAttendanceDTOs) {
                Date attDate = DateUtils.formatPunchTimeToDate(abnormalAttendanceDTO.getPunchTime1(), abnormalAttendanceDTO.getDateStr());
                abnormalAttendanceDTO.setAttendenceDate(attDate);
                if (abnormalAttendanceDTO.getState() == 1) {
                    abnormalAttendanceDTOs_insert.add(abnormalAttendanceDTO);
                } else if (abnormalAttendanceDTO.getState() == 2) {
                    abnormalAttendanceDTO.setUpdateTime(new Date());
                    abnormalAttendanceDTOs_update.add(abnormalAttendanceDTO);
                }
            }

            int insertSize = abnormalAttendanceDTOs_insert.size() > 200 ? 200 : abnormalAttendanceDTOs_insert.size();
            int updateSize = abnormalAttendanceDTOs_update.size() > 200 ? 200 : abnormalAttendanceDTOs_update.size();
            List<List<AbnormalAttendanceDTO>> attendenceInsertList = CommonUtils.averageAssign(abnormalAttendanceDTOs_insert, insertSize);
            List<List<AbnormalAttendanceDTO>> attendenceUpdateList = CommonUtils.averageAssign(abnormalAttendanceDTOs_update, updateSize);
            for (List<AbnormalAttendanceDTO> abnormalAttendanceDTOList : attendenceInsertList) {
                if (abnormalAttendanceDTOList.size() > 0) {
                    attendenceMapper.insertAttendenceAbnormal(abnormalAttendanceDTOList);
                    attendenceCacheService.saveOrUpdateAttendenceAbnormalToRedis(abnormalAttendanceDTOList);
                }
            }

            for (List<AbnormalAttendanceDTO> abnormalAttendanceDTOList : attendenceUpdateList) {
                if (abnormalAttendanceDTOList.size() > 0) {
                    attendenceMapper.updateAbnormalList(abnormalAttendanceDTOList);
                    attendenceCacheService.dropAttendence(CacheNameConstant.ATTENDANCE_ABNORMAL_LIST);
                }
            }
            if (abnormalAttendanceDTOs_update.size() > 0) {
                attendenceCacheService.dropAttendence(CacheNameConstant.ATTENDANCE_ABNORMAL_LIST);
            }

        }

        return abnormalAttendanceDTOs;
    }

    /**
     * 保存考勤正常数据到数据库
     * 先过滤出所有考勤异常的数据
     * 然后在所有的考勤中排除异常的数据
     * 剩下考勤正常数据
     *
     * @param aliWorkDTOs
     * @param abnormalAttendanceDTOs
     */
    public void saveAttendanceNormal(List<AliWorkDTO> aliWorkDTOs, List<AbnormalAttendanceDTO> abnormalAttendanceDTOs) throws CommonException, ParseException {
        List<AliWorkDTO> aliWorkDTOsCopy = filterAliWorkChange(aliWorkDTOs);
        //只有state为1的时候保存
        //保存考勤时会查询人员信息表 如果不存在就设置state为0;
        for (AbnormalAttendanceDTO abnormalAttendanceDTO : abnormalAttendanceDTOs) {
            //全部考勤和异常考勤列表以aliNumber和考勤日期对关联
            //排除state为0的情况
            //在全部考勤中删除异常考勤
            //剩下考勤正常数据;
            aliWorkDTOsCopy.removeIf(aliWorkDTO -> aliWorkDTO.getAliNumber().equals(abnormalAttendanceDTO.getAliNumber()) && aliWorkDTO.getDateStr().equals(abnormalAttendanceDTO.getDateStr()));
        }

        List<AliWorkDTO> insertList = new ArrayList<AliWorkDTO>();
        List<AliWorkDTO> updateList = new ArrayList<AliWorkDTO>();
        if (aliWorkDTOsCopy.size() > 0) {
            for (AliWorkDTO aliWorkDTO : aliWorkDTOsCopy) {
                int state = aliWorkDTO.getState();
                //将日期字符串转换成考勤日期
                Date attDate = DateUtils.formatPunchTimeToDate(aliWorkDTO.getPunchTime1(), aliWorkDTO.getDateStr());
                aliWorkDTO.setAttendenceDate(attDate);
                if (state == 1) {
                    insertList.add(aliWorkDTO);
                } else if (state == 2) {
                    aliWorkDTO.setUpdateTime(new Date());
                    updateList.add(aliWorkDTO);
                }
            }

            int insertSize = insertList.size() > 200 ? 200 : insertList.size();
            int updateSize = updateList.size() > 200 ? 200 : updateList.size();
            List<List<AliWorkDTO>> attendenceInsertList = CommonUtils.averageAssign(insertList, insertSize);
            List<List<AliWorkDTO>> attendenceUpdateList = CommonUtils.averageAssign(updateList, updateSize);
            for (List<AliWorkDTO> insertDBList : attendenceInsertList) {
//                if(insertDBList.get(0).getEmployeeName().equals("王芸")){
//                    int a = 12;
//                }
                if (insertDBList.size() > 0) {
                    try {
                        attendenceMapper.insertAttendenceNormal(insertDBList);
                    } catch (Exception e) {
                        log.error("保存正常考勤数据失败:" + e.getMessage());
                    }

                    attendenceCacheService.saveOrUpdateAttendenceNormalToRedis(insertDBList);
                }
            }

            for (List<AliWorkDTO> attendenceUpdateBDList : attendenceUpdateList) {
                if (attendenceUpdateBDList.size() > 0) {
                    attendenceMapper.updateNormalList(attendenceUpdateBDList);
                }
            }
            if (updateList.size() > 0) {
                attendenceCacheService.dropAttendence(CacheNameConstant.ATTENDANCE_NORMAL_LIST);
            }
        }
    }

    /**
     * 过滤考勤信息state有变化的数据
     * 并且在人员表中有此人的考勤
     *
     * @param aliWorkDTOs
     * @return
     */
    private List<AliWorkDTO> filterAliWorkChange(List<AliWorkDTO> aliWorkDTOs) {
        List<AliWorkDTO> aliWorkDTOsCopy = new ArrayList<>();
        if(aliWorkDTOs!=null&&aliWorkDTOs.size()>0){
            for (AliWorkDTO aliWorks : aliWorkDTOs) {
                if (aliWorks.getState() > 0) {
                    EmployeesDO empByAliOrName = employeesService.findEmpByNumOrName(aliWorks.getAliNumber(), aliWorks.getWorkNum(), aliWorks.getEmployeeName());
                    if (empByAliOrName != null) {
                        aliWorkDTOsCopy.add(aliWorks);
                    }
                }
            }
        }
        return aliWorkDTOsCopy;
    }

    /**
     * 保存考勤的全部数据
     *
     * @param aliWorkDTOs
     * @throws ParseException
     * @throws CommonException
     */
    private void saveMySQL(List<AliWorkDTO> aliWorkDTOs) throws ParseException, CommonException {
        //新增考勤list
        List<AttendenceCommonDO> attendenceCommonDOs = new ArrayList<>();
        List<AttendenceCountDO> attendenceCountDOs = new ArrayList<>();

        //需要修改的考勤list
        List<AttendenceCommonDO> attendenceCommonDOs_update = new ArrayList<>();
        List<AttendenceCountDO> attendenceCountDOs_update = new ArrayList<>();

        for (AliWorkDTO aliWorkDTO : aliWorkDTOs) {
            //考勤日期
            Date attDate = DateUtils.parseDateStrToDate(aliWorkDTO.getDateStr());
            if (attDate != null) {
                EmployeesDO empByAliOrName = employeesService.findEmpByNumOrName(aliWorkDTO.getAliNumber(), aliWorkDTO.getWorkNum(), aliWorkDTO.getEmployeeName());
                int state = aliWorkDTO.getState();
                //将考勤对象转换成数据库存储对象
                AttendenceCommonDO attendenceCommonDO = new AttendenceCommonDO();
                AttendenceCountDO attendenceCountDO = new AttendenceCountDO();
                BeanUtils.copyProperties(aliWorkDTO, attendenceCommonDO);
                BeanUtils.copyProperties(aliWorkDTO, attendenceCountDO);
                attendenceCommonDO.setOrders(aliWorkDTO.getOrder());
                attendenceCommonDO.setAttendanceDate(attDate);
                //将考勤对象转换成数据库存储对象 end
                //新增考勤
                if (empByAliOrName != null) {
                    //设置commId和countId
                    AttendanceCheckDO attendanceCheckDO = new AttendanceCheckDO();
                    attendanceCheckDO.setAttendanceDate(attDate);
                    attendanceCheckDO.setAliNumber(aliWorkDTO.getAliNumber());
                    attendanceCheckDO.setEmployeeName(aliWorkDTO.getEmployeeName());
                    Integer commId = attendenceMapper.findCommIdByEmpId(attendanceCheckDO);
                    if (commId != null) {
                        AttendanceCondition attendanceCondition = new AttendanceCondition();
                        attendanceCondition.setCommId(commId);
                        Integer countId = attendenceMapper.findCountIdByCommId(attendanceCondition);
                        attendenceCommonDO.setId(commId);
                        attendenceCountDO.setId(countId);
                    }
                    //设置commId和countId结束
                    aliWorkDTO.setEmpId(empByAliOrName.getEmpId());
                    attendenceCommonDO.setEmpId(empByAliOrName.getEmpId());

                    if (state == 1) {
                        attendenceCommonDOs.add(attendenceCommonDO);
                        attendenceCountDOs.add(attendenceCountDO);
                    } else if (state == 2) {
                        attendenceCommonDOs_update.add(attendenceCommonDO);
                        attendenceCountDOs_update.add(attendenceCountDO);
                    } else {
                        //人员表里没有此员工设置状态为了在mongoDB中不插入数据
                        aliWorkDTO.setState(0);
                        log.error("人员信息表中没有找到此员工:" + aliWorkDTO);
                    }
                }
            } else {
                log.error("员工考勤日期为空:" + aliWorkDTO);
            }


        }
        /*
         *  如果有考勤保存 就删除redis里的考勤
         *   一次性插入数据太多 将list 分组;
         * */
        if (attendenceCommonDOs.size() > 0 && attendenceCountDOs.size() > 0) {
            saveCommonAndCount(attendenceCommonDOs, attendenceCountDOs);

        }

        /**
         *  跟新考勤state为2的数据
         */
        //当跟新考勤信息时同时需要同步redis
        //将保存list和修改list 合并在一起
        if (attendenceCommonDOs_update.size() > 0 && attendenceCountDOs_update.size() > 0) {
            updateCommonAndCount(attendenceCommonDOs_update, attendenceCountDOs_update);
        }


//        /**
//         *  保存DU到数据库
//         */
        saveDU(attendenceCommonDOs);



    }


    /**
     * 保存新增的考勤
     *
     * @param attendenceCommonDOs
     * @param attendenceCountDOs
     * @throws CommonException
     */
    private void saveCommonAndCount(List<AttendenceCommonDO> attendenceCommonDOs, List<AttendenceCountDO> attendenceCountDOs) throws CommonException {
        //设置分组长度
        int size = attendenceCommonDOs.size() > 200 ? 200 : attendenceCommonDOs.size();
        List<List<AttendenceCommonDO>> attendenceCommonDOList = CommonUtils.averageAssign(attendenceCommonDOs, size);
        log.info("批量插入数据库 总共list:" + attendenceCommonDOList);
        for (List l : attendenceCommonDOList) {
            log.error("批量插入数据库 其中一个list:" + l);
            if (l.size() != 0) {
                attendenceMapper.insertCommonList(l);
                log.info("保存到attendence_common的考勤数据:" + l);
                attendenceCacheService.saveOrUpdateAttendenceToRedis(l);
            } else {
                throw new CommonException(ErrCodeConstant.ILLEGAL_PARAM, "没有要保存的考勤数据");
            }
        }
        //给attencence_count 设置主键
        for (int i = 0; i < attendenceCountDOs.size(); i++) {
            AttendenceCountDO attendenceCountDO = attendenceCountDOs.get(i);
            attendenceCountDO.setAttId(attendenceCommonDOs.get(i).getId());
        }
        List<List<AttendenceCountDO>> attendenceCountDOList = CommonUtils.averageAssign(attendenceCountDOs, size);
        for (List l : attendenceCountDOList) {
            if (l.size() != 0) {
                attendenceMapper.insertCountList(l);
                log.info("保存到attendence_count的考勤数据:" + l);
            }

        }
        log.info("---------保存考勤数据到数据库结束-----------");
    }

    /**
     * 保存DU到mysql
     *
     * @param attendenceCommonDOs 需要保存的考勤对象
     */
    private void saveDU(List<AttendenceCommonDO> attendenceCommonDOs) throws CommonException {
        if (attendenceCommonDOs != null && attendenceCommonDOs.size() > 0) {
            Set<DUDO> dudoSet = new HashSet<>();
            deelRepeatDU(attendenceCommonDOs, dudoSet);
            saveDUtoDB(dudoSet);
        }
    }

    /**
     * 处理是否有重复的du
     * @param attendenceCommonDOs     考勤对象列表
     * @param dudoSet                   du set
     */
    private void deelRepeatDU(List<AttendenceCommonDO> attendenceCommonDOs,Set<DUDO> dudoSet){
        List<DUDO> dudos = duService.findAllDu();
        for(AttendenceCommonDO attendenceCommonDO : attendenceCommonDOs){
            if(attendenceCommonDO!=null){
                DUDO dudo = new DUDO();
                String du  = attendenceCommonDO.getDu();
                String aliNumber = attendenceCommonDO.getAliNumber();
                if(!StringUtils.isEmpty(du)&&!StringUtils.isEmpty(aliNumber)){
                    dudo.setDu(attendenceCommonDO.getDu());
                    dudo.setAliNumber(attendenceCommonDO.getAliNumber());
                    dudo.setCreateTime(new Date());
                    if(!dudos.contains(dudo)) {
                        dudoSet.add(dudo);
                    }
                }
            }
        }
    }

    /**
     * 保存du 到数据库
     * @param dudoSet    没有重复的du列表
     */
    private void saveDUtoDB(Set<DUDO> dudoSet){
        int size = dudoSet.size() > 20 ? 20 : dudoSet.size();
        List<DUDO> duList = new ArrayList<DUDO>(dudoSet);
        List<List<DUDO>> attendenceCommonDOList = CommonUtils.averageAssign(duList, size);
        for (List<DUDO> l : attendenceCommonDOList) {
            if(l!=null){
                if (l.size() != 0) {
                    duService.insertDu(l);
                } else {
                    log.error("保存DU时,没有要保存的考勤数据");
                }
            }
        }
    }

    /**
     * 修改考勤
     * 注意:修改考勤之后redis需要跟新
     *
     * @param attendenceCommonDOs
     * @param attendenceCountDOs
     */

    @Transactional
    private void updateCommonAndCount(List<AttendenceCommonDO> attendenceCommonDOs, List<AttendenceCountDO> attendenceCountDOs) throws CommonException {
        int commSize = attendenceCommonDOs.size() > 200 ? 200 : attendenceCommonDOs.size();
        int countSize = attendenceCommonDOs.size() > 200 ? 200 : attendenceCommonDOs.size();
        //设置跟新时间
        for (AttendenceCommonDO attendenceCommonDO : attendenceCommonDOs) {
            attendenceCommonDO.setUpdateTime(new Date());
        }
        for (AttendenceCountDO AttendenceCountDO : attendenceCountDOs) {
            AttendenceCountDO.setUpdateTime(new Date());
        }
        List<List<AttendenceCommonDO>> attendenceCommonDOList = CommonUtils.averageAssign(attendenceCommonDOs, commSize);
        List<List<AttendenceCountDO>> attendenceCountDOList = CommonUtils.averageAssign(attendenceCountDOs, countSize);
        for (List<AttendenceCommonDO> list : attendenceCommonDOList) {
            if (list.size() > 0) {
                attendenceMapper.updateCommonList(list);
            }

        }

        for (List<AttendenceCountDO> list : attendenceCountDOList) {
            if (list.size() > 0) {
                attendenceMapper.updateCountList(list);
            }
        }
        attendenceCacheService.dropAttendence(CacheNameConstant.ATTENDANCE_LIST);
    }

}